<template>
    <!-- 主内容 -->
    <div class="review">
        <!-- 页头部分 -->
        <div class="menu">
            <div class="flex items-center">
                <el-upload ref="uploadRef"
                           class="video-upload"
                           :auto-upload="false"
                           :show-file-list="false"
                           :limit="1"
                           @change="fileSelected">
                    <template #trigger>
                        <el-button v-btn type="primary">选择视频文件</el-button>
                    </template>
                </el-upload>
            </div>
            <el-button type="warning" @click="dialogUploadVideoVisible = true" :loading="isLoading" v-btn style="margin-left: 10px">协同复盘</el-button>
            <el-button type="success" @click="screenshot" v-btn style="margin-left: 10px">复盘当前帧</el-button>
            <el-input v-model="remoteUrl" style="width: 320px; margin-left: 10px" placeholder="请粘贴BV视频地址或复盘码"/>
            <el-button type="info" @click="loadUrl" :loading="isLoading" v-btn style="margin-left: 10px">读取视频</el-button>
        </div>
        <p class="desc">选择视频文件播放, 按下 <b>空格暂停</b> <b>左右箭头快进快退</b>, 需要复盘某一帧时, 点击复盘当前帧即可打开复盘窗口</p>
        <p class="desc">正在播放：<b>{{ fileName }}</b></p>
        <!-- 视频播放器 -->
        <div id="dplayer" ref="dplayerContainer" class="dplayerContainer" @keydown="onKeyPress"></div>
    </div>
    <!-- 复盘弹窗 -->
    <el-dialog v-model="dialogCorrectVisible" fullscreen @close="closeDialog">
        <div class="dialogCorrectBox">
            <div class="actionBar">

                <el-button type="primary" plain v-btn @click="VueCanvasDrawing.undo()">撤销</el-button>
                <el-button type="primary" plain v-btn @click="VueCanvasDrawing.redo()">重做</el-button>
                <el-button type="primary" plain v-btn @click="VueCanvasDrawing.reset()">清空</el-button>
                <el-button type="primary" plain v-btn @click="fieldCheck">场地识别</el-button>

                <span style="margin-left: 10px">颜色：</span>
                <el-color-picker v-model="lineColor"/>

                <!-- 快捷颜色 -->
                <div class="fastColor">
                    <div class="colorBlock" style="background-color: #1afe93" @click="lineColor = blockColor.I">I</div>
                    <div class="colorBlock" style="background-color: #f13837" @click="lineColor = blockColor.O">O</div>
                    <div class="colorBlock" style="background-color: #c35af6" @click="lineColor = blockColor.S">S</div>
                    <div class="colorBlock" style="background-color: #f98643" @click="lineColor = blockColor.Z">Z</div>
                    <div class="colorBlock" style="background-color: #9be742" @click="lineColor = blockColor.L">L</div>
                    <div class="colorBlock" style="background-color: #2e87fd" @click="lineColor = blockColor.J">J</div>
                    <div class="colorBlock" style="background-color: #fed52d" @click="lineColor = blockColor.T">T</div>
                </div>

                <!-- 快捷颜色 -->
                <div class="fastColor">
                    <div class="colorBlock" style="background-color: #000000" @click="lineColor = '#000000'">黑</div>
                    <div class="colorBlock" style="background-color: #666666" @click="lineColor = '#666666'">灰</div>
                    <div class="colorBlock" style="background-color: #ffffff;color: #000000;border: none" @click="lineColor = '#ffffff'">白</div>
                </div>

                <!-- 快捷线宽 -->
                <span style="margin-left: 10px">线宽：</span>
                <el-input-number v-model="lineWidth" :min="1" :max="20"></el-input-number>

            </div>
            <p class="desc" style="text-align: center; margin: 0"><b style="color: red">{{ modelTitle }}</b></p>
            <p class="desc" style="text-align: center">按下 <b>ESC</b> 可关闭当前复盘窗口</p>
            <div class="canvas-box">
                <div class="canvas-overlay" ref="canvasOverlay" v-if="isShowCorrectOverlay">
                    <div class="overlay-mask" ref="overlayMask" :style="{ width: maskWidth + 'px', height: maskHeight + 'px'}"></div>
                </div>
                <div class="result-overlay" v-if="isShowResultOverlay">
                    <div class="matrix">
                        <div class="matrix-row" v-for="(row, index) in selectMatrix">
                            <div class="matrix-cell" v-for="(cell, index) in row" :class="{ 'matrix-cell-selected': cell }"></div>
                        </div>
                    </div>
                    <div class="right">
                        <div class="image">
                            <img :src="backgroundCorrectImage" alt="">
                        </div>
                        <div class="techmino-string">
                            <el-input
                                    v-model="techminoStr"
                                    style="width: 100%;"
                                    type="textarea"
                                    placeholder="场地字符串"
                            />
                        </div>
                    </div>
                </div>
                <vue-drawing-canvas ref="VueCanvasDrawing"
                                    :width="1024"
                                    :height="576"
                                    :background-image="backgroundCorrectImage"
                                    strokeType="dash"
                                    lineCap="square"
                                    :color="lineColor"
                                    :lineWidth="lineWidth"
                                    :styles="{ border: 'solid 1px #000' }"
                                    lineJoin="round"></vue-drawing-canvas>
            </div>
        </div>
    </el-dialog>
    <!-- 选集弹窗 -->
    <el-dialog v-model="dialogSelectVideoVisible" title="视频选集" @close="dialogSelectVideoVisible = false">
        <el-alert title="因为视频需要回传到服务器, 点击选集后请耐心等待视频回传完毕, 请勿加载1小时以上的视频" type="info" :closable="false" style="margin-bottom: 10px;"/>
        <el-row :gutter="20">
            <el-col :span="8" v-for="(item, index) in videoInfo">
                <el-button style="width: 100%;margin-bottom: 10px;overflow: hidden;" :loading="isLoading" @click="playVideo(item)">
                    <div style="overflow: hidden;white-space: nowrap;text-overflow: ellipsis; width: 200px; -webkit-line-clamp:1;">
                        P{{ item.page }} {{ item.part }}
                    </div>
                </el-button>
            </el-col>
        </el-row>
    </el-dialog>
    <!-- 协同复盘上传 -->
    <el-dialog v-model="dialogUploadVideoVisible" title="协同复盘" @close="dialogUploadVideoVisible = false" destroy-on-close :width="500" :close-on-click-modal="false">
        <el-alert title="新功能 <协同复盘> 震撼来袭！可以上传您需要复盘的视频，并生成一个复盘码，视频会为您保留7天，您可以将复盘码发给朋友，对方在快否页面上可以使用复盘码直接加载该视频！" type="success" :closable="false" effect="dark"/>
        <el-input style="margin-top: 10px;margin-bottom: 5px" placeholder="当前为内测阶段, 为防止上传不良视频, 请在此处填入内测码" v-model="invCode"></el-input>
        <el-upload :auto-upload="false"
                   v-if="xtUploadStatus === 0"
                   ref="xtUploadRef"
                   style="margin-top: 10px"
                   :limit="1"
                   drag
                   list-type="text"
                   accept="video/*"
                   v-model:file-list="fileList"
                   :on-exceed="handleExceed">
            <el-icon class="el-icon--upload">
                <upload-filled/>
            </el-icon>
            <div class="el-upload__text">
                <div v-if="fileList.length <= 0">
                    将文件拖拽到此处或 <em>点一下选择文件</em>
                </div>
                <div v-else>
                    已选择视频文件 {{ fileList[0].name }}
                </div>
            </div>
            <template #tip>
                <div class="el-upload__tip">
                    支持上传不大于2GB的常见视频文件格式
                </div>
            </template>
        </el-upload>
        <div class="upload-process" v-if="xtUploadStatus === 1">
            <el-progress type="dashboard"
                         :percentage="uploadPercentage"
                         :status="uploadPercentage == 100 ? 'success' : ''">
                <template #default="{ percentage }">
                    <span class="percentage-value">{{ percentage }}%</span>
                    <span class="percentage-label">{{ percentage == 100 ? '上传完毕' : '上传中' }}</span>
                </template>
            </el-progress>
            <div class="tips">
                <span>正在上传中, 在上传完毕前请不要关闭本窗口</span>
            </div>
        </div>
        <div class="review-code" v-if="xtUploadStatus === 2">
            <div>当前视频复盘码</div>
            <div class="code">
                {{ reviewCode }}
            </div>
            <span class="tips">复制上方复盘码发给好友, 邀请他打开快否给你复盘吧</span>
        </div>
        <template #footer>
            <div class="dialog-footer">
                <el-button @click="dialogUploadVideoVisible = false" v-btn>取消</el-button>
                <el-button type="primary" @click="xtUploadFile" v-btn :loading="isLoading">
                    <span v-if="xtUploadStatus === 0">立即上传</span>
                    <span v-else-if="xtUploadStatus === 1">正在上传</span>
                    <span v-else>再次上传</span>
                </el-button>
            </div>
        </template>
    </el-dialog>
</template>

<script setup>

import DPlayer from 'dplayer';
import { nextTick, onMounted, ref } from "vue";
import VueDrawingCanvas from "vue-drawing-canvas";
import * as tetrApi from "@/api/tetr.js";
import { ElMessage } from "element-plus";
import { UploadFilled } from '@element-plus/icons-vue'
import OSS from "ali-oss";
import { Base64 } from "js-base64";

// 播放器实例
let dplayer = null;

// 远端视频地址
let remoteUrl = ref('')

// 协同待上传的视频
const fileList = ref([])

// 上传组件的引用
const xtUploadRef = ref(null);

// 画布实例
const VueCanvasDrawing = ref(null);

// 是否加载
const isLoading = ref(false)

// 加载的视频列表
const videoInfo = ref([]);

// 上传进度值
const uploadPercentage = ref(0);

// 协同上传状态 0-等待上传 1-上传中 2-上传完毕
const xtUploadStatus = ref(0)

// 协同复盘码
const reviewCode = ref('')

// 协同内测码
const invCode = ref('')

// 颜色表
const blockColor = {
    I: '#1afe93',
    O: '#f13837',
    S: '#c35af6',
    Z: '#f98643',
    L: '#9be742',
    J: '#2e87fd',
    T: '#fed52d',
}

// 是否显示复盘拖拽层
const isShowCorrectOverlay = ref(false);
const isShowResultOverlay = ref(false); // 显示识别结果区域
const overlayMask = ref(null); // 识别区域拖动层
const canvasOverlay = ref(null); // 识别区域引用

// 已识别的可绘制像素数据
let selectMatrix = [];
let maskWidth = ref(0); // 识别区域宽度
let maskHeight = ref(0); // 识别区域高度
let techminoStr = ref(''); // 铁壳场地字符串

const uploadRef = ref();
const dplayerContainer = ref();
const dialogCorrectVisible = ref(false);
const dialogSelectVideoVisible = ref(false)
const dialogUploadVideoVisible = ref(false)
const backgroundCorrectImage = ref('');
const lineColor = ref('#000000');
const lineWidth = ref(8);
const modelTitle = ref('当前帧复盘');
const fileName = ref('未选择视频文件');

/**
 * 创建视频地址
 * @param file
 * @returns {null}
 */
const createUrl = (file) => {
    let url = null;
    if ( window.createObjcectURL !== undefined ) url = window.createObjcectURL(file);
    else if ( window.URL !== undefined ) url = window.URL.createObjectURL(file);
    else if ( window.webkitURL !== undefined ) url = window.webkitURL.createObjectURL(file);
    return url;
}

/**
 * 文件被选中
 * @param e
 */
const fileSelected = (e) => {

    // 选择完毕加载到当前的播放器中
    if ( e.status === 'ready' ) {
        const file = e.raw;
        const fileUrl = createUrl(file);
        fileName.value = file.name;
        dplayer.switchVideo({ url: fileUrl });
    }

    // 移除已选中的文件
    uploadRef.value.clearFiles();
}

/**
 * 复盘当前帧
 */
const screenshot = () => {

    // 创建当前帧的图片
    let canvas = document.createElement("canvas") // 创建canvas节点
    let w = window.innerWidth
    let h = (window.innerWidth / 16) * 9
    canvas.width = w
    canvas.height = h // 设置宽高
    const ctx = canvas.getContext("2d")
    ctx.drawImage(dplayer.video, 0, 0, w, h)

    // 获取视频当前的播放时间(并将秒转为时间格式)
    const currentTime = dplayer.video.currentTime;
    const minutes = Math.floor(currentTime / 60);
    const seconds = Math.floor(currentTime % 60);
    const time = `${ minutes }:${ seconds < 10 ? '0' + seconds : seconds }`;

    // 标题写当前时间
    modelTitle.value = `正在复盘 ${ time } 时间点`;
    backgroundCorrectImage.value = canvas.toDataURL("image/png")

    // 暂停播放
    dplayer.pause();

    // 打开弹窗
    dialogCorrectVisible.value = true;

    // 关闭识别遮罩
    isShowCorrectOverlay.value = false;
    isShowResultOverlay.value = false;

    if ( VueCanvasDrawing.value != null ) {
        VueCanvasDrawing.value.reset();
        VueCanvasDrawing.value.redraw();
    }
}

/**
 * 快捷键处理
 * @param e
 */
const onKeyPress = (e) => {
    console.warn(e)
}

/**
 * 关闭弹窗清空画布还原颜色为黑色
 */
const closeDialog = () => {
    lineColor.value = '#000000';
    dialogCorrectVisible.value = false;
    dplayerContainer.value.focus()
}

/**
 * 从视频地址加载视频
 */
const loadUrl = () => {
    // 视频地址不能为空
    if ( remoteUrl.value === '' ) {
        alert('请输入需要播放的视频地址')
    }

    // 如果视频是以Review:开头则获取后面的内容并解开Base64
    if ( remoteUrl.value.startsWith('Review:') ) {
        const code = remoteUrl.value.replace('Review:', '');
        const ossFileName = Base64.decode(code);
        const fullUrl = `https://cdn.bk.loval.ink/${ ossFileName }`
        fileName.value = `复盘码 => ${ code }`;
        dplayer.switchVideo({ url: fullUrl });
        dplayer.play();
        return;
    }

    // 从视频链接读取一条视频信息播放
    isLoading.value = true;
    tetrApi.bvGetPlayerUrlInfo({ bvUrl: remoteUrl.value }).then(res => {
        videoInfo.value = res.data
        dialogSelectVideoVisible.value = true
    }).catch(err => ElMessage.error(err.message)).finally(() => isLoading.value = false);
}

/**
 * 由服务器中转的BV视频播放
 * @param item
 */
const playVideo = (item) => {
    isLoading.value = true;
    tetrApi.bvHashGetStreamFileUrl({ hash: item.url_hash }).then(res => {
        dialogSelectVideoVisible.value = false
        fileName.value = `BVID => ${ item.bvid } P${ item.page } ${ item.part }`;
        dplayer.switchVideo({ url: res.data.url });
        dplayer.play();
    }).catch(err => ElMessage.error(err.message)).finally(() => isLoading.value = false);
}

/**
 * 识别当前场地
 */
const fieldCheck = () => {

    // 任意一个在打开的时候都关闭
    if ( isShowCorrectOverlay.value || isShowResultOverlay.value ) {
        isShowCorrectOverlay.value = false
        isShowResultOverlay.value = false;
        return;
    }

    // 打开识别遮罩
    isShowCorrectOverlay.value = true;
    ElMessage.success({ message: '请从场地左上角开始拖拽，选中整个场地' })

    // 重置状态
    selectMatrix = [];

    if ( isShowCorrectOverlay.value ) {

        // 移除全部已绘制层
        if ( VueCanvasDrawing.value != null ) {
            VueCanvasDrawing.value.reset();
            VueCanvasDrawing.value.redraw();
        }

        nextTick(() => {
            let isMouseDown = false
            canvasOverlay.value.addEventListener('mousedown', (e) => {

                isMouseDown = true

                // 显示拖拽框
                maskWidth.value = 0;
                maskHeight.value = 0;
                overlayMask.value.style.display = 'block'
                overlayMask.value.style.left = `${ e.offsetX }px`
                overlayMask.value.style.top = `${ e.offsetY }px`

            })

            // 拖拽动作
            canvasOverlay.value.addEventListener('mousemove', (e) => {
                if ( isMouseDown ) {
                    maskWidth.value = e.offsetX - parseInt(overlayMask.value.style.left)
                    maskHeight.value = (e.offsetX - parseInt(overlayMask.value.style.left)) * 2
                }
            })

            // 松开鼠标
            canvasOverlay.value.addEventListener('mouseup', () => {

                isMouseDown = false

                const left = parseInt(overlayMask.value.style.left)
                const top = parseInt(overlayMask.value.style.top)
                const width = parseInt(overlayMask.value.style.width)
                const height = parseInt(overlayMask.value.style.height)

                // 画布对象
                const canvas = document.getElementById("VueDrawingCanvas")
                const canvasCtx = canvas.getContext("2d")

                // 根据宽高计算每个格子的大小
                const cellWidth = width / 10
                const cellHeight = height / 20
                for ( let i = 0; i < 20; i++ ) { // 从上到下扫描20行
                    selectMatrix.push([])
                    for ( let j = 0; j < 10; j++ ) { // 每一行扫描10列
                        // 单个格子的数据
                        const cellData = canvasCtx.getImageData(left + j * cellWidth, top + i * cellHeight, cellWidth, cellHeight)
                        const cellPixels = cellData.data;

                        // 求平均RGB值
                        let aColorR = 0;
                        let aColorG = 0;
                        let aColorB = 0;

                        // 总像素数量
                        const allPixels = cellPixels.length / 4;

                        // 像素求和值
                        let sumColorR = 0;
                        let sumColorG = 0;
                        let sumColorB = 0;

                        // 对当前格子的RGB进行求和
                        for ( let k = 0; k < cellPixels.length; k += 4 ) {
                            sumColorR += cellPixels[k];
                            sumColorG += cellPixels[k + 1];
                            sumColorB += cellPixels[k + 2];
                        }

                        // 取得最终RGB值
                        aColorR = Math.round(sumColorR / allPixels + 20);
                        aColorG = Math.round(sumColorG / allPixels + 20);
                        aColorB = Math.round(sumColorB / allPixels + 20);

                        // 求得RGB平均值
                        let gValue = Math.round((aColorR + aColorG + aColorB) / 3);

                        // 降低亮度
                        gValue = Math.max(0, gValue - 120);
                        if ( gValue !== 0 ) {
                            gValue = 1; // 有格子的场地
                        }

                        // 输出到场地矩阵
                        selectMatrix[i].push(gValue)
                    }
                }

                // 场地选择关闭
                isShowCorrectOverlay.value = false;
                isShowResultOverlay.value = true

                // 请求矩阵到铁壳字符串
                techminoStr.value = '';
                tetrApi.matrixToTechmino({ matrix: selectMatrix }).then(res => {
                    techminoStr.value = res.data.field
                })
            })
        })
    }
}

/**
 * 上传视频文件超出限制
 * @param files
 * @param newFileList
 */
const handleExceed = (files, newFileList) => {
    xtUploadRef.value.clearFiles()
    nextTick(() => {
        xtUploadRef.value.handleStart(files[0])
    })
}

/**
 * 协同视频上传
 */
const xtUploadFile = () => {

    // 如果上传状态=2则重置为0
    if ( xtUploadStatus.value === 2 ) {
        xtUploadStatus.value = 0
        fileList.value = [];
        return;
    }

    // 检查文件列表
    if ( fileList.value.length <= 0 ) {
        alert('请选择需要上传的视频文件')
        return;
    }

    // 文件信息
    const file = fileList.value[0];

    // 如果文件列表不为空则检查是否大于2G
    if ( file.raw.size > 2 * 1024 * 1024 * 1024 ) {
        alert('文件大小超过2GB, 请重新选择')
        return;
    }
    // 不是视频格式的文件
    if ( file.raw.type.indexOf('video') === -1 ) {
        alert('请选择视频文件')
        return;
    }

    // 从文件名中获取后缀
    const ext = file.name.split('.').pop();

    // 申请上传Token并开始上传视频
    isLoading.value = true;
    tetrApi.uploadGetReviewCode({ invCode: invCode.value }).then(async res => {

        // 后端传来的配置
        const conf = {
            region: res.data.region,
            accessKeyId: res.data.accessKeyId,
            accessKeySecret: res.data.accessKeySecret,
            stsToken: res.data.stsToken,
            bucket: res.data.bucket,
        }

        // 上传到OSS的文件名和配置
        const name = `${ res.data.fileName }.${ ext }`
        const options = {
            progress: (p, cpt, res) => {
                // 保留两位小数
                uploadPercentage.value = (p * 100).toFixed(2)
            },
            parallel: 2, // 设置并发上传的分片数量
            partSize: 1024 * 1024 * 1, // 设置分片大小 默认值为1MB 最小值为100 KB
        };

        // 建立客户端并开始发起上传
        try {
            const client = new OSS(conf);
            // noinspection JSUnresolvedReference
            xtUploadStatus.value = 1
            const multipartUploadRes = client.multipartUpload(name, file.raw, {
                ...options,
                // callback: {
                //     url: res.data.callbackUrl,
                //     contentType: "application/x-www-form-urlencoded",
                //     body: "bucket=${bucket}&ossName=${object}",
                //     customValue: {},
                // },
            }).then((result) => {
                xtUploadStatus.value = 2
                isLoading.value = false
                reviewCode.value = `Review:${ Base64.encode(name) }`
            }).catch(err => {
                ElMessage.error(err.message)
                isLoading.value = false
                xtUploadStatus.value = 0
            })
        } catch ( e ) {
            ElMessage.error(e.message)
            isLoading.value = false
            xtUploadStatus.value = 0
        }
    }).catch(err => {
        ElMessage.error(err.message)
        isLoading.value = false
        xtUploadStatus.value = 0
    })
}

onMounted(() => {

    // 挂载后初始化视频播放器
    dplayer = new DPlayer({
        container: document.getElementById('dplayer'),
        playbackSpeed: [ 0.5, 0.75, 1, 1.25, 1.5, 2 ],
        autoplay: false,
        theme: '#FADFA3',
        loop: true,
        lang: 'zh-cn',
        screenshot: true,
        hotkey: false,
        preload: 'none',
        volume: 0.7,
        mutex: true,
        video: { type: 'auto' },
    });

    // 挂载完成后初始化快捷键
    document.addEventListener('keydown', (event) => {
        if ( dplayer.focus ) {
            switch ( event.keyCode ) {
                case 32:
                    event.preventDefault();
                    dplayer.toggle();
                    break;
                case 37:
                    event.preventDefault();
                    if ( dplayer.options.live ) {
                        break;
                    }
                    // 快退1秒 按住Ctrl快退5秒
                    dplayer.seek(dplayer.video.currentTime - (event.ctrlKey ? 5 : 1));
                    break;
                case 39:
                    event.preventDefault();
                    if ( dplayer.options.live ) {
                        break;
                    }
                    // 快进1秒 按住Ctrl快进5秒
                    dplayer.seek(dplayer.video.currentTime + (event.ctrlKey ? 5 : 1));
                    break;
            }
        }
    });
})

</script>

<style lang="scss">
// 页头按钮强制靠左
.el-page-header__header {
    justify-content: flex-start !important;
}

.el-upload-list {
    display: none !important;
}
</style>

<style scoped lang="scss">

// 主内容
.review {
    width: 100%;
    margin-top: 10px;
}

// 按钮菜单
.menu {
    display: flex;
    align-items: center;
    justify-content: flex-start;

    el-button {
        margin-left: 10rpx;
    }
}

// 文件名
.fileName {
    text-align: center;
    margin-bottom: 20px;
}

// 视频播放器
.dplayerContainer {
    width: 100%;
}

// 纠错框
.dialogCorrectBox {

    width: 100%;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;

    // 弹窗图片样式
    .dialogCorrectImage {
        width: 100%;
        user-select: none;
        -webkit-user-drag: none;
    }

    // 画布容器
    .canvas-box {
        width: 1024px;
        height: 576px;
        display: flex;
        align-items: center;
        justify-content: center;
        position: relative;

        .canvas-overlay {
            top: 0;
            left: 0;
            width: 1024px;
            height: 576px;
            background: rgba(0, 0, 0, 0.24);
            z-index: 1000;
            position: absolute;

            .overlay-mask {
                width: 200px;
                height: 200px;
                position: absolute;
                border: 2px solid red;
                display: none;
                user-select: none;
            }
        }

        .result-overlay {
            top: 0;
            left: 0;
            width: 1024px;
            height: 576px;
            background: rgba(0, 0, 0, 0.24);
            z-index: 1000;
            position: absolute;
            display: flex;
            justify-content: center;
            align-items: center;
            background: #fff;

            .matrix {
                .matrix-row {
                    display: flex;

                    .matrix-cell {
                        background: #000;
                        width: 28.8px;
                        height: 28.8px;

                        &.matrix-cell-selected {
                            background: #f2f2f2;
                        }
                    }
                }
            }

            .right {
                display: flex;
                align-items: flex-start;
                justify-content: flex-start;
                flex-direction: column;
                height: 100%;

                .image {
                    width: 100%;

                    img {
                        width: 736px;
                    }
                }

                .techmino-string {
                    flex: 1;
                    width: 736px;
                    height: 150px;
                }
            }
        }
    }
}

// 纠错框的操作条
.actionBar {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 20px;

    .fastColor {
        display: flex;
        align-items: center;
        justify-content: center;
        margin-left: 10px;

        .colorBlock {
            width: 30px;
            height: 30px;
            display: flex;
            align-items: center;
            justify-content: center;
            border: 1px solid;
            user-select: none;
            color: #ffffff;
            background-color: dimgrey;
        }
    }
}

// 页头描述
.desc {
    font-size: 13px;
    text-align: left;
}

// 上传进度
.upload-process {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: 55px;
    margin-bottom: 35px;
    flex-direction: column;

    .percentage-value {
        display: block;
        margin-top: 10px;
        font-size: 24px;
    }

    .percentage-label {
        display: block;
        margin-top: 10px;
        font-size: 12px;
    }

    .demo-progress .el-progress--line {
        margin-bottom: 15px;
        max-width: 600px;
    }

    .demo-progress .el-progress--circle {
        margin-right: 15px;
    }

    .tips {
        margin-top: 10px;
        font-size: 12px;
        color: #666;
    }
}

.review-code {
    margin-top: 20px;
    text-align: center;
    font-size: 16px;
    color: #666;
    font-weight: bold;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;

    .code {
        background: #eaeaea;
        padding: 10px 30px;
        margin-top: 20px;
        margin-bottom: 20px;
        display: block;
        color: #27a524;
    }

    .tips {
        font-size: 12px;
        color: #666;
    }
}

</style>
